#include "headers/gdt.h"
#include "../api/headers/dataTypes.h"
// Lets us access our ASM functions from our C code.
extern void gdtFlush(u32int);

// Internal function prototypes.
static void gdtSetGate(s32int num, u32int base, u32int limit, u8int access, u8int gran);
u8int getGranularityByte(BOOL granularity, BOOL opSize, u8int segLength);
u8int getAccessByte(BOOL present, u8int dpl, BOOL dType, BOOL exec, BOOL direction, BOOL rw);

GdtEntry	gdtEntries[5];
GdtPtr		gdtPtr;

void initGdt()
{
	u8int gran;
	
	gdtPtr.limit = (sizeof(GdtEntry) * 5) - 1;
	gdtPtr.base  = (u32int)&gdtEntries;
	gran = getGranularityByte(1, 1, 0xFF);

	gdtSetGate(0, 0, 0, 0, 0);					// Null segment
	gdtSetGate(1, 0, 0xFFFFFFFF, getAccessByte(1, 0, 1, 1, 0, 1), gran); 	// Code segment
	gdtSetGate(2, 0, 0xFFFFFFFF, getAccessByte(1, 0, 1, 0, 0, 1), gran); 	// Data segment
	gdtSetGate(3, 0, 0xFFFFFFFF, getAccessByte(1, 3, 1, 1, 0, 1), gran); 	// User mode code segment
	gdtSetGate(4, 0, 0xFFFFFFFF, getAccessByte(1, 3, 1, 0, 0, 1), gran); 	// User mode data segment

	gdtFlush((u32int)&gdtPtr);
}

// Set the value of one GDT entry.
static void gdtSetGate(s32int num, u32int base, u32int limit, u8int access, u8int gran)
{
	gdtEntries[num].baseLow = (base & 0xFFFF);
	gdtEntries[num].baseMiddle = (base >> 16) & 0xFF;
	gdtEntries[num].baseHigh = (base >> 24) & 0xFF;

	gdtEntries[num].limitLow = (limit & 0xFFFF);
	gdtEntries[num].granularity = (limit >> 16) & 0x0F;

	gdtEntries[num].granularity |= gran & 0xF0;
	gdtEntries[num].access = access;
}

u8int getAccessByte(BOOL present, u8int dpl, BOOL dType, BOOL exec, BOOL direction, BOOL rw)
{
	u8int res = 0;
	res |= ((present?1:0) << 7) & 0b10000000;
	res |= (dpl << 5) & 0b01100000;
	res |= ((dType?1:0) << 4) & 0b00010000;
	res |= ((exec?1:0) << 3) & 0b00001000;
	res |= ((direction?1:0) << 2) & 0b00000100;
	res |= ((rw?1:0) << 1) & 0b00000010;
	return res;
}

u8int getGranularityByte(BOOL granularity, BOOL opSize, u8int segLength) //No segment length part (lower 4 bits) - add manually
{
	u8int res = 0;
	res |= ((granularity?1:0) << 7) & 0b10000000;
	res |= ((opSize?1:0) << 6) & 0b01000000;
	res |=segLength & 0b00001111;
	return res;
}
